package com.ssu.sslab;
import java.net.*;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Iterator;
import java.io.*;

import com.ssu.sslab.sensorInfo.SensorsInfo;
import com.ssu.sslab.sensorInfo.SensorsInfoSetting;
import com.ssu.sslab.typeConvertor.TypeConvertor;


public class ServerSocketThread extends Thread implements SensorsInfoSetting{
	
	private Socket socket = null;
	private DataInputStream dis = null;
	private DataOutputStream dos = null;
	private SensorsInfo sensorsInfo;
	private ArrayList<SensorsInfo> sensorsInfos;
	private int sequenceNum = 0;
	
	public ServerSocketThread(Socket socket,ArrayList<SensorsInfo> sensorsInfos) {
		this.socket = socket;
		this.sensorsInfos = sensorsInfos;
		new TypeConvertor();
		try {
			dis = new DataInputStream(this.socket.getInputStream());
			dos = new DataOutputStream(this.socket.getOutputStream());
		} catch (IOException e) {
			
			e.printStackTrace();
		}
		start();
	}
	
	//=============================================================================================================
	/**
	 *                              Data Format In Transmission [unit: byte]
	 * ============================================================================================================
	 * |sequence | sensorID | sensorName | valueType | value | description | ServerIP | date[YYYY/MM/DD-hh:mm:ss] |
	 * ============================================================================================================
	 * |    4    |     4    |  8(fixed)  |    1      |Uncertain|  10(fixed) |   8     |            7              |
	 * ============================================================================================================
	 * 
	 * The value's size dues to the valueType;
	 * The sensorName and description are all fixed also.
	 * 
	 * @author chengqiang sun,system software lab in Soongsil University. 
	 * @since 2012.11.13
	 */
	//==============================================================================================================
	
	public void run() {

//		byte [] sendBuf = new byte[512];
//		byte [] receiveBuf = new byte[512];
		
		System.out.println("Connected: ok");
		System.out.println("=================================");
		
//		int countBuf = 0;// Check the receive buffer size.
		
		while( true ) {
			byte [] sendBuf = new byte[512];
			byte [] receiveBuf = new byte[512];

			try {
				int readCount = 0;
				// ensure getting all the 512 bytes data.
				while (readCount < 512) {
					readCount += dis.read(receiveBuf, readCount, 512-readCount);
					
				}

//				countBuf = dis.read(receiveBuf);
				int ack = receiveBuf[0];
				int Methodkey = receiveBuf[2];
				int checksum = receiveBuf[3];
				int data = TypeConvertor.SetByteArrayToInt(receiveBuf, 4);
				System.out.println( "receive buffer : " + readCount+"  ack : "+ ack);
				if( readCount == 512 ) { // the receive buffer is not empty.
					
					//Check the ACK, SYN ,FIN bit.
					switch (ack) {
					
					// alive check, [ ACK = 1,SYN =1,FIN=0 ] Send to Client.
					case 2:  
						sendBuf[0] = (byte)6;
						dos.write(sendBuf);
						dos.flush();
						break;
					//Data transfer [ACK = 1,SYN = 0,FIN = 0] Received from Client.
					case 4:
						System.out.println("MethodKey : "+ Methodkey);
						//==========================================================
						switch (Methodkey) {
						
						case 1:// Client requests one sensor information by ID
							sendBuf[0] = 6;
							sendBuf[1] = 0;
							sendBuf[2] = (byte)1;
							sendBuf[3] = (byte)(checksum+1);
							setSensorToByteArray(sendBuf, data, 4);
							dos.write(sendBuf);
							dos.flush();
							break;
						case 2:// Client requests all sensors information.
							sendBuf[0] = (byte)6;
							sendBuf[1] = (byte)1;
							sendBuf[2] = (byte)2;
							sendBuf[3] = (byte)sensorsInfos.size();
							for (int i = 0; i < sensorsInfos.size(); i++) {
								setAllSensorsToArray(sendBuf, sensorsInfos.get(i));
								dos.write(sendBuf);
								dos.flush();
							}
							break;
						case 3:// Client cancels one sensor by ID.
							break;
						case 4:// Client cancels all sensors.
							break;
						}
						//===========================================================
					
					// Transmission end [ACK = 1,SYN = 0,FIN = 1]
					case 5:
						break;
					}
				}
				Thread.sleep(1000);
			} catch (IOException e) {				
				System.out.println("Error inside thread : " + e);
				System.exit(1);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
			
		}
		
	}

	
	
	

	@Override
	public void setSensorToByteArray(byte[] sendBuf, int sensorID, int start) {
		// TODO Auto-generated method stub
		
		Iterator<SensorsInfo> it = sensorsInfos.iterator();
		while (it.hasNext()) {
			sensorsInfo = it.next();
			if (sensorsInfo.getSensorID() == sensorID) {
				sequenceNum += 1;
				System.out.println(sequenceNum);
				TypeConvertor.SetValueToByteArray(sendBuf, 4, sequenceNum);
				TypeConvertor.SetValueToByteArray(sendBuf, 8, sensorsInfo.getSensorID());
				TypeConvertor.SetValueToByteArray(sendBuf, 12, sensorsInfo.getSensorName());
				
				switch (sensorsInfo.getValueType()) {
				case INT:
					TypeConvertor.SetValueToByteArray(sendBuf, 20, (byte)0);
					TypeConvertor.SetValueToByteArray(sendBuf, 21, (int)sensorsInfo.getValue());
					TypeConvertor.SetValueToByteArray(sendBuf, 25, sensorsInfo.getDescription());
					setIpToByteArray(sendBuf, 35);//8 bytes ip address;
					setDateToByteArray(sendBuf, 43);// 7 bytes date;
					break;
				case SHORT:
					TypeConvertor.SetValueToByteArray(sendBuf, 20, (byte)1);
					TypeConvertor.SetValueToByteArray(sendBuf, 21, (short)sensorsInfo.getValue());
					TypeConvertor.SetValueToByteArray(sendBuf, 23, sensorsInfo.getDescription());
					setIpToByteArray(sendBuf, 33);//8 bytes ip address;
					setDateToByteArray(sendBuf, 41);// 7 bytes date;
					break;
				case LONG:
					TypeConvertor.SetValueToByteArray(sendBuf, 20, (byte)2);
					TypeConvertor.SetValueToByteArray(sendBuf, 21, (long)sensorsInfo.getValue());
					TypeConvertor.SetValueToByteArray(sendBuf, 29, sensorsInfo.getDescription());
					setIpToByteArray(sendBuf, 39);//8 bytes ip address;
					setDateToByteArray(sendBuf, 47);// 7 bytes date;
					break;
				case DOUBLE:
					TypeConvertor.SetValueToByteArray(sendBuf, 20, (byte)3);
					TypeConvertor.SetValueToByteArray(sendBuf, 21, (double)sensorsInfo.getValue());
					TypeConvertor.SetValueToByteArray(sendBuf, 29, sensorsInfo.getDescription());
					setIpToByteArray(sendBuf, 39);//8 bytes ip address;
					setDateToByteArray(sendBuf, 47);// 7 bytes date;
					break;
				case FLOAT:
					TypeConvertor.SetValueToByteArray(sendBuf, 20, (byte)4);
					TypeConvertor.SetValueToByteArray(sendBuf, 21, (float)sensorsInfo.getValue());
					TypeConvertor.SetValueToByteArray(sendBuf, 25, sensorsInfo.getDescription());
					setIpToByteArray(sendBuf, 35);//8 bytes ip address;
					setDateToByteArray(sendBuf, 43);// 7 bytes date;
					break;
				case BYTE:
					TypeConvertor.SetValueToByteArray(sendBuf, 20, (byte)5);
					TypeConvertor.SetValueToByteArray(sendBuf, 21, (byte)sensorsInfo.getValue());
					TypeConvertor.SetValueToByteArray(sendBuf, 22, sensorsInfo.getDescription());
					setIpToByteArray(sendBuf, 32);//8 bytes ip address;
					setDateToByteArray(sendBuf, 40);// 7 bytes date;
					break;
				case STRING:
					String str = "";
					int strLength = 0;
					TypeConvertor.SetValueToByteArray(sendBuf, 20, (byte)6);
					strLength = TypeConvertor.SetValueToByteArray(sendBuf, 22, str+sensorsInfo.getValue());
					sendBuf[21] = (byte) strLength;
					TypeConvertor.SetValueToByteArray(sendBuf, 22+strLength, sensorsInfo.getDescription());
					setIpToByteArray(sendBuf, 32+strLength);//8 bytes ip address;
					setDateToByteArray(sendBuf, 40+strLength);// 7 bytes date;
					break;
				case BOOLEAN:
					TypeConvertor.SetValueToByteArray(sendBuf, 20, (byte)7);
					TypeConvertor.SetValueToByteArray(sendBuf, 21, (boolean)sensorsInfo.getValue());
					TypeConvertor.SetValueToByteArray(sendBuf, 22, sensorsInfo.getDescription());
					setIpToByteArray(sendBuf, 32);//8 bytes ip address;
					setDateToByteArray(sendBuf, 40);// 7 bytes date;
					break;
				}
			}
			
		}
	}

	@Override
	public void setAllSensorsToArray(byte[] sendBuf,SensorsInfo sensorInfo) {
		// TODO Auto-generated method stub
//		Iterator<SensorsInfo> it = sensorsInfos.iterator();
//		while (it.hasNext()) {
//			sensorsInfo = it.next();

				sequenceNum += 1;
				TypeConvertor.SetValueToByteArray(sendBuf, 4, sequenceNum);
				TypeConvertor.SetValueToByteArray(sendBuf, 8, sensorInfo.getSensorID());
				TypeConvertor.SetValueToByteArray(sendBuf, 12, sensorInfo.getSensorName());
				
				switch (sensorInfo.getValueType()) {
				case INT:
					TypeConvertor.SetValueToByteArray(sendBuf, 20, (byte)0);
					TypeConvertor.SetValueToByteArray(sendBuf, 21, (int)sensorInfo.getValue());
					TypeConvertor.SetValueToByteArray(sendBuf, 25, sensorInfo.getDescription());
					setIpToByteArray(sendBuf, 35);//8 bytes ip address;
					setDateToByteArray(sendBuf, 43);// 7 bytes date;
					break;
				case SHORT:
					TypeConvertor.SetValueToByteArray(sendBuf, 20, (byte)1);
					TypeConvertor.SetValueToByteArray(sendBuf, 21, (short)sensorInfo.getValue());
					TypeConvertor.SetValueToByteArray(sendBuf, 23, sensorInfo.getDescription());
					setIpToByteArray(sendBuf, 33);//8 bytes ip address;
					setDateToByteArray(sendBuf, 41);// 7 bytes date;
					break;
				case LONG:
					TypeConvertor.SetValueToByteArray(sendBuf, 20, (byte)2);
					TypeConvertor.SetValueToByteArray(sendBuf, 21, (long)sensorInfo.getValue());
					TypeConvertor.SetValueToByteArray(sendBuf, 29, sensorInfo.getDescription());
					setIpToByteArray(sendBuf, 39);//8 bytes ip address;
					setDateToByteArray(sendBuf, 47);// 7 bytes date;
					break;
				case DOUBLE:
					TypeConvertor.SetValueToByteArray(sendBuf, 20, (byte)3);
					TypeConvertor.SetValueToByteArray(sendBuf, 21, (double)sensorInfo.getValue());
					TypeConvertor.SetValueToByteArray(sendBuf, 29, sensorInfo.getDescription());
					setIpToByteArray(sendBuf, 39);//8 bytes ip address;
					setDateToByteArray(sendBuf, 47);// 7 bytes date;
					break;
				case FLOAT:
					TypeConvertor.SetValueToByteArray(sendBuf, 20, (byte)4);
					TypeConvertor.SetValueToByteArray(sendBuf, 21, (float)sensorInfo.getValue());
					TypeConvertor.SetValueToByteArray(sendBuf, 25, sensorInfo.getDescription());
					setIpToByteArray(sendBuf, 35);//8 bytes ip address;
					setDateToByteArray(sendBuf, 43);// 7 bytes date;
					break;
				case BYTE:
					TypeConvertor.SetValueToByteArray(sendBuf, 20, (byte)5);
					TypeConvertor.SetValueToByteArray(sendBuf, 21, (byte)sensorInfo.getValue());
					TypeConvertor.SetValueToByteArray(sendBuf, 22, sensorInfo.getDescription());
					setIpToByteArray(sendBuf, 32);//8 bytes ip address;
					setDateToByteArray(sendBuf, 40);// 7 bytes date;
					break;
				case STRING:
					String str = "";
					int strLength = 0;
					TypeConvertor.SetValueToByteArray(sendBuf, 20, (byte)6);
					strLength = TypeConvertor.SetValueToByteArray(sendBuf, 21, str+sensorInfo.getValue());
					TypeConvertor.SetValueToByteArray(sendBuf, 21+strLength, sensorInfo.getDescription());
					setIpToByteArray(sendBuf, 31+strLength);//8 bytes ip address;
					setDateToByteArray(sendBuf, 39+strLength);// 7 bytes date;
					break;
				case BOOLEAN:
					TypeConvertor.SetValueToByteArray(sendBuf, 20, (byte)7);
					TypeConvertor.SetValueToByteArray(sendBuf, 21, (boolean)sensorInfo.getValue());
					TypeConvertor.SetValueToByteArray(sendBuf, 22, sensorInfo.getDescription());
					setIpToByteArray(sendBuf, 32);//8 bytes ip address;
					setDateToByteArray(sendBuf, 40);// 7 bytes date;
					break;
				}
//		}
	}

	@Override
	public void setIpToByteArray(byte[] sendBuf, int start) {
		// TODO Auto-generated method stub
		try {
			InetAddress addr = Inet4Address.getLocalHost();
			String ip = addr.getHostAddress();
			for (int i = 0; i < (ip.split("\\.")).length; i++) {
				TypeConvertor.SetValueToByteArray(sendBuf, start+i*2, (short)Integer.parseInt((ip.split("\\."))[i]));
				
			}
		} catch (UnknownHostException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	@Override
	public void setDateToByteArray(byte[] sendBuf, int start) {
		
		Calendar calendar = Calendar.getInstance();
		short year = (short)calendar.get(Calendar.YEAR);// YEAR:2 bytes
		byte month = (byte)(calendar.get(Calendar.MONTH)+1);//MONDAY: 1 byte
		byte day = (byte)(calendar.get(Calendar.DATE));//DATE: 1 byte
		byte hour = (byte)calendar.get(Calendar.HOUR_OF_DAY);//HOUR: 1 byte
		byte minute = (byte)calendar.get(Calendar.MINUTE);//MINUTE: 1 byte
		byte second = (byte)calendar.get(Calendar.SECOND);//SECOND: 1 byte

		TypeConvertor.SetValueToByteArray(sendBuf, start, year);
		TypeConvertor.SetValueToByteArray(sendBuf, start+2,month );
		TypeConvertor.SetValueToByteArray(sendBuf, start+3, day);
		TypeConvertor.SetValueToByteArray(sendBuf, start+4, hour);
		TypeConvertor.SetValueToByteArray(sendBuf, start+5, minute);
		TypeConvertor.SetValueToByteArray(sendBuf, start+6, second);
		
	}

	@Override
	public void CancelSpecifiedSensor(int sensor) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void CancelAllSensors() {
		// TODO Auto-generated method stub
		
	}
}
